﻿namespace com.oakwoodsys.ConsultantCare
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    using com.oakwoodsys.ConsultantCareDao;
    using com.oakwoodsys.Extensions.DateTimeExtensions;
    using com.oakwoodsys.Extensions.DoubleExtensions;
    using com.oakwoodsys.Extensions.SharePointExtensions;
    using com.oakwoodsys.SharePoint;

    using Microsoft.SharePoint;

    public class CalendarEventCreator
    {
        #region Fields

        const int ACTIVITYSCHEDULEDMINUTES = 15;
        const string DOBACTIVITYMESSAGE = "Birthday";
        const string DOBACTIVITYTITLE = "Birthday";
        const int DOBREMINDERDAYS = 3;
        const string DOBREMINDERMESSAGE = "Birthday Reminder";
        const string DOBREMINDERTITLE = "Birthday Reminder";
        const int MAXYEARS = 25;

        ConsultantCareActivity _DobActivity;
        ConsultantCareActivity _DobReminder;

        #endregion Fields

        #region Constructors

        public CalendarEventCreator( string webUrl )
        {
            WebUrl = webUrl;
        }

        #endregion Constructors

        #region Properties

        ConsultantCareConfiguration configuration
        {
            get
            {
                ConsultantCareConfigurationDao dao = new ConsultantCareConfigurationDao( WebUrl );
                ConsultantCareConfiguration o = dao.Get( );
                if ( o == null )
                    throw new Exception( "Consultant Care is not configured." );
                else if ( string.IsNullOrEmpty( o.CalendarPathUrl ) )
                    throw new Exception( "Consultant Care master calendar Url is not configured." );
                if ( !o.MaxYears.HasValue || ( o.MaxYears.HasValue && o.MaxYears.Value <= 0 ) )
                    o.MaxYears = MAXYEARS;
                return o;
            }
        }

        ConsultantCareActivity DobActivity
        {
            get
            {
                if ( _DobActivity == null )
                    _DobActivity = new ConsultantCareActivity
                    {
                        Active = true,
                        Associated = Associated.Both,
                        Message = string.IsNullOrEmpty( configuration.DOBActivityMessage ) ? DOBACTIVITYMESSAGE : configuration.DOBActivityMessage,
                        NDays = 1,
                        TimeInterval = TimeInterval.Years,
                        Title = string.IsNullOrEmpty( configuration.DOBActivityTitle ) ? DOBACTIVITYTITLE : configuration.DOBActivityTitle,
                        UniqueId = new Guid( "19e6d1ec-72c8-47a4-9bc9-1242dcccde99" )
                    };
                return _DobActivity;
            }
        }

        ConsultantCareActivity DobReminder
        {
            get
            {
                if ( _DobReminder == null )
                    _DobReminder = new ConsultantCareActivity
                    {
                        Active = true,
                        Associated = Associated.Both,
                        Message = string.IsNullOrEmpty( configuration.DOBReminderMessage ) ? DOBREMINDERMESSAGE : configuration.DOBReminderMessage,
                        NDays = 1,
                        TimeInterval = TimeInterval.Years,
                        Title = string.IsNullOrEmpty( configuration.DOBReminderTitle ) ? DOBREMINDERTITLE : configuration.DOBReminderTitle,
                        UniqueId = new Guid( "8c4e3a42-e752-436e-93ca-41dafba4dfbf" )
                    };
                return _DobReminder;
            }
        }

        string WebUrl
        {
            get;
            set;
        }

        #endregion Properties

        #region Methods

        public void AddCalendarEvents( List<Consultant> consultants, List<ConsultantCareActivity> activities )
        {
            foreach ( Consultant consultant in consultants )
                AddCalendarEvents( consultant, activities );
        }

        public void AddCalendarEvents( Consultant consultant, List<ConsultantCareActivity> activities )
        {
            Recruiter recruiter = getAssociatedRecruiter( consultant );
            if ( recruiter != null )
            {
                string calendarPathUrl = recruiter.CalendarPathUrl;
                string masterCalendarPathUrl = configuration.CalendarPathUrl;

                List<CalendarEvent> calendarEvents = createCalendarEvents( consultant, activities );

                foreach ( CalendarEvent calendarEvent in calendarEvents )
                {
                    // recruiter calendar
                    SharePointUri recruiterCalendarUri = new SharePointUri( recruiter.CalendarPathUrl );
                    Guid calendarEventID = addCalendarEvent( calendarEvent, calendarPathUrl, recruiterCalendarUri );
                    addRelationship( consultant.UniqueId, calendarEvent.ActivityID, calendarEventID, recruiter.UniqueId, calendarPathUrl );

                    // master calendar
                    SharePointUri masterCalendarUri = new SharePointUri( configuration.CalendarPathUrl );
                    if ( !masterCalendarUri.ListUrl.Equals( recruiterCalendarUri.ListUrl, StringComparison.OrdinalIgnoreCase ) )
                    {
                        Guid masterCalendarEventID = addCalendarEvent( calendarEvent, masterCalendarPathUrl, masterCalendarUri );
                        addRelationship( consultant.UniqueId, calendarEvent.ActivityID, masterCalendarEventID, recruiter.UniqueId, masterCalendarPathUrl );
                    }
                }
            }
        }

        public void RemoveCalendarEvents( List<Consultant> consultants )
        {
            foreach ( Consultant consultant in consultants )
                RemoveCalendarEvents( consultant );
        }

        public void RemoveCalendarEvents( Consultant consultant )
        {
            RemoveCalendarEventsByConsultant( consultant.UniqueId );
        }

        public void RemoveCalendarEvents( ConsultantCareActivity activity )
        {
            RemoveCalendarEventsByActivity( activity.UniqueId );
        }

        public void RemoveCalendarEvents( Recruiter recruiter )
        {
            RemoveCalendarEventsByRecruiter( recruiter.UniqueId );
        }

        public void RemoveCalendarEventsByActivity( Guid activityID )
        {
            List<ConsultantActivityID> list = new List<ConsultantActivityID>( );
            List<ConsultantActivityID> relationships = getRelationshipByActivity( activityID );
            foreach ( ConsultantActivityID relationship in relationships )
            {
                removeCalendarEvent( new Guid( relationship.CalendarEventID ), relationship.Title );
                list.Add( relationship );
            }
            removeRelationships( relationships );
        }

        public void RemoveCalendarEventsByConsultant( Guid consultantID )
        {
            List<ConsultantActivityID> list = new List<ConsultantActivityID>( );
            List<ConsultantActivityID> relationships = getRelationshipByConsultant( consultantID );
            foreach ( ConsultantActivityID relationship in relationships )
            {
                removeCalendarEvent( new Guid( relationship.CalendarEventID ), relationship.Title );
                list.Add( relationship );
            }
            removeRelationships( relationships );
        }

        public void RemoveCalendarEventsByRecruiter( Guid recruiterID )
        {
            List<ConsultantActivityID> list = new List<ConsultantActivityID>( );
            List<ConsultantActivityID> relationships = getRelationshipByRecruiter( recruiterID );
            foreach ( ConsultantActivityID relationship in relationships )
            {
                removeCalendarEvent( new Guid( relationship.CalendarEventID ), relationship.Title );
                list.Add( relationship );
            }
            removeRelationships( relationships );
        }

        Guid addCalendarEvent( CalendarEvent calendarEvent, string url, SharePointUri calendarUri )
        {
            CalendarEventDao dao = new CalendarEventDao( url, calendarUri );
            return dao.Add( calendarEvent );
        }

        void addRelationship( Guid consultantID, Guid activityID, Guid calendarEventID, Guid recruiterID, string calendarPathUrl )
        {
            ConsultantActivityID value = new ConsultantActivityID
            {
                ActivityID = activityID.ToString( ),
                CalendarEventID = calendarEventID.ToString( ),
                ConsultantID = consultantID.ToString( ),
                RecruiterID = recruiterID.ToString( ),
                Title = calendarPathUrl
            };
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            dao.Add( value );
        }

        DateTime adjustBackwardIfWeekend( DateTime timeDate )
        {
            DateTime date = timeDate;
            // if Saturday or Sunday, then move to Friday
            if ( date.DayOfWeek == DayOfWeek.Saturday )
                date = date.SubtractDays( 1 );
            else if ( date.DayOfWeek == DayOfWeek.Sunday )
                date = date.SubtractDays( 2 );

            return date;
        }

        DateTime adjustForwardIfWeekend( DateTime timeDate )
        {
            DateTime date = timeDate;
            // if Saturday or Sunday, then move to Monday
            if ( date.DayOfWeek == DayOfWeek.Saturday )
                date = date.AddDays( 2 );
            else if ( date.DayOfWeek == DayOfWeek.Sunday )
                date = date.AddDays( 1 );

            return date;
        }

        List<CalendarEvent> createCalendarEvents( Consultant consultant, List<ConsultantCareActivity> activities )
        {
            List<CalendarEvent> calendarEvents = new List<CalendarEvent>( );
            foreach ( ConsultantCareActivity activity in activities )
            {
                DateTime d = consultant.StartDate.Value;
                DateTime startDate = new DateTime( d.Year, d.Month, d.Day, 8, 0, 0 );

                if ( activity.Recurring.HasValue && activity.Recurring.Value )
                {
                    calendarEvents.AddRange( createRecurringEvents( startDate, activity, consultant ) );
                }
                else
                {
                    DateTime eventDate = getEventDate( startDate, activity );

                    if ( eventDate.Date >= DateTime.Now.Date )
                    {
                        DateTime endDate = eventDate.AddMinutes( ACTIVITYSCHEDULEDMINUTES );
                        calendarEvents.Add( createEvent( eventDate, endDate, activity, consultant ) );
                    }
                }
            }

            if ( configuration.DOBActivityActive.HasValue && configuration.DOBActivityActive.Value )
                if ( consultant.DOB.HasValue )
                {
                    if ( !isConsultantDobReminderSet( consultant.UniqueId ) )
                        calendarEvents.AddRange( createRecurringEvents( getDOBReminderStartDate( consultant.DOB.Value ), DobReminder, consultant ) );
                    if ( !isConsultantDobActivitySet( consultant.UniqueId ) )
                        calendarEvents.AddRange( createRecurringEvents( getDOBStartDate( consultant.DOB.Value ), DobActivity, consultant ) );
                }

            return calendarEvents;
        }

        CalendarEvent createEvent( DateTime eventDate, DateTime endDate, ConsultantCareActivity activity, Consultant consultant )
        {
            return new CalendarEvent
            {
                ActivityID = activity.UniqueId,
                AllDayEvent = true,
                Category = "Consultant Care",
                Description = getMessage( activity, consultant ),
                EndDate = endDate,
                StartTime = eventDate,
                Title = getTitle( activity, consultant )
            };
        }

        List<CalendarEvent> createRecurringEvents( DateTime startDate, ConsultantCareActivity activity, Consultant consultant )
        {
            List<CalendarEvent> calendarEvents = new List<CalendarEvent>( );

            DateTime maxDate = startDate.AddYears( configuration.MaxYears.Value.ToInt32( ) );

            bool ok = true;
            int i = 0;
            while ( ok )
            {
                i++;
                DateTime eventDate = getEventDate( startDate, activity, i );

                if ( eventDate >= DateTime.Now )
                {
                    DateTime endDate = eventDate.AddMinutes( ACTIVITYSCHEDULEDMINUTES );
                    if ( eventDate > maxDate )
                    {
                        ok = false;
                        break;
                    }
                    calendarEvents.Add( createEvent( eventDate, endDate, activity, consultant ) );
                }
            }
            return calendarEvents;
        }

        Recruiter getAssociatedRecruiter( Consultant consultant )
        {
            RecruiterDao dao = new RecruiterDao( WebUrl );
            return dao.GetByRecruiter( consultant.RecruiterId.Value );
        }

        DateTime getDOBReminderStartDate( DateTime consultantDOB )
        {
            DateTime now = DateTime.Now;
            DateTime dob = consultantDOB;
            if ( dob.DayOfYear > now.DayOfYear )
                dob = new DateTime( now.Year - 1, dob.Month, dob.Day );
            else
                dob = new DateTime( now.Year, dob.Month, dob.Day );
            int x = configuration.DOBReminderDays.HasValue && configuration.DOBReminderDays.Value > 0 ? configuration.DOBReminderDays.Value.ToInt32( ) : DOBREMINDERDAYS;
            return dob.SubtractDays( x );
        }

        DateTime getDOBStartDate( DateTime consultantDOB )
        {
            DateTime now = DateTime.Now;
            DateTime dob = consultantDOB;
            if ( dob.DayOfYear > now.DayOfYear )
                dob = new DateTime( now.Year - 1, dob.Month, dob.Day );
            else
                dob = new DateTime( now.Year, dob.Month, dob.Day );
            return dob;
        }

        DateTime getEventDate( DateTime startDate, ConsultantCareActivity activity )
        {
            return getEventDate( startDate, activity, 1 );
        }

        DateTime getEventDate( DateTime startDate, ConsultantCareActivity activity, int multiplier )
        {
            TimeInterval timeInterval = activity.TimeInterval.HasValue ? activity.TimeInterval.Value : TimeInterval.Days;

            int n = activity.NDays.Value.ToInt32( );
            // adjust for 0 days
            int j = n == 0 ? 1 : 0;

            if ( n == 0 )
                n = 1;
            DateTime eventDate = DateTime.Now;

            // DAYS
            if ( timeInterval == TimeInterval.Days )
            {
                DateTime currentDate = startDate.AddDays( n * multiplier ).SubtractDays( j );

                if ( activity.IsReminder.HasValue && activity.IsReminder.Value )
                    // activity is a reminder: adjust backward
                    eventDate = adjustBackwardIfWeekend( currentDate );

                else
                    // activity is NOT a reminder: adjust forward
                    eventDate = adjustForwardIfWeekend( currentDate );
            }
            // MONTHS
            else if ( timeInterval == TimeInterval.Months )
            {
                DateTime currentDate = startDate.AddMonths( n * multiplier );

                if ( activity.IsReminder.HasValue && activity.IsReminder.Value )
                    // activity is a reminder: adjust backward
                    eventDate = adjustBackwardIfWeekend( currentDate );

                else
                    // activity is NOT a reminder: adjust forward
                    eventDate = adjustForwardIfWeekend( currentDate );
            }
            // YEARS
            else if ( timeInterval == TimeInterval.Years )
            {
                DateTime currentDate = startDate.AddYears( n * multiplier );

                if ( activity.UniqueId.Equals( DobActivity.UniqueId ) )
                    // DOB activity: no date adjustment
                    eventDate = currentDate;

                else if ( activity.UniqueId.Equals( DobReminder.UniqueId ) )
                    // DOB reminder: adjust backward
                    eventDate = adjustBackwardIfWeekend( currentDate );

                else if ( activity.IsReminder.HasValue && activity.IsReminder.Value )
                    // activity is a reminder: adjust backward
                    eventDate = adjustBackwardIfWeekend( currentDate );

                else
                    // activity is NOT a reminder: adjust forward
                    eventDate = adjustForwardIfWeekend( currentDate );
            }

            return eventDate;
        }

        string getMessage( ConsultantCareActivity activity, Consultant consultant )
        {
            const string NOTICE = "Any changes to telephone numbers or to the email address should be communicated to your manager.";

            StringBuilder sb = new StringBuilder( );
            sb.AppendFormat( "{0}<br />", activity.Message );
            sb.AppendFormat( "<b>Consultant:</b> {0}<br />", consultant.Title );
            if ( !string.IsNullOrEmpty( consultant.ClientName ) )
                sb.AppendFormat( "<b>Client:</b> {0}<br />", consultant.ClientName );
            sb.AppendFormat( "<b>Email:</b> {0}<br />", consultant.EmailAddress );
            if ( !string.IsNullOrEmpty( consultant.TelMobile ) )
                sb.AppendFormat( "<b>Telephone-Mobile:</b> {0}<br />", consultant.TelMobile );
            if ( !string.IsNullOrEmpty( consultant.TelOther ) )
                sb.AppendFormat( "<b>Telephone-Other:</b> {0}<br />", consultant.TelOther );
            sb.AppendFormat( "<b>Start date:</b> {0}<br />", consultant.StartDate.Value.ToShortDateString( ) );

            sb.AppendFormat( "<b>Recruiter:</b> {0}<br />", consultant.AssociatedRecruiter.UserInfo.Name );

            sb.AppendFormat( "<br />{0}<br />", consultant.ItemUrl );

            sb.AppendFormat( "<br /><b>{0}</b>", NOTICE );

            string s = sb.ToString( ).Trim( );
            sb = null;
            return s;
        }

        List<ConsultantActivityID> getRelationshipByActivity( Guid activityID )
        {
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            return dao.GetByActivity( activityID );
        }

        List<ConsultantActivityID> getRelationshipByConsultant( Guid consultantID )
        {
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            return dao.GetByConsultant( consultantID );
        }

        bool isConsultantDobActivitySet( Guid consultantID )
        {
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            return dao.DoesActivityExistForConsultant( consultantID, DobActivity.UniqueId );
        }

        bool isConsultantDobReminderSet( Guid consultantID )
        {
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            return dao.DoesActivityExistForConsultant( consultantID, DobReminder.UniqueId );
        }

        List<ConsultantActivityID> getRelationshipByRecruiter( Guid recruiterID )
        {
            ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
            return dao.GetByRecruiter( recruiterID );
        }

        string getTitle( ConsultantCareActivity activity, Consultant consultant )
        {
            StringBuilder sb = new StringBuilder( );
            sb.AppendFormat( "{0} :: ", consultant.Title );
            if ( !string.IsNullOrEmpty( consultant.ClientName ) )
                sb.AppendFormat( "{0} :: ", consultant.ClientName );
            sb.AppendFormat( "{0}", activity.Title );
            string s = sb.ToString( ).Trim( );
            sb = null;
            return s;
        }

        void removeCalendarEvent( Guid calendarID, string calendarPathUrl )
        {
            SharePointUri uri = new SharePointUri( calendarPathUrl );
            CalendarEventDao dao = new CalendarEventDao( calendarPathUrl, uri );
            dao.Remove( calendarID );
        }

        void removeRelationships( List<ConsultantActivityID> relationships )
        {
            for ( int i = relationships.Count - 1; i >= 0; i-- )
            {
                ConsultantActivityIDDao dao = new ConsultantActivityIDDao( WebUrl );
                dao.Delete( relationships[ i ].UniqueId );
            }
        }

        #endregion Methods
    }
}